/**
 * Covalent Server Code File
 * 
 * File:        transaction.c
 * Brief:       BRIEF
 * Author:      Chen Kangbing
 * Email:       ckblau@outlook.com
 * Modified:    2021/10/09 09:28:56
 *
 */


#include "transaction.h"
#include "protocol.h"
#include "thread_helper.h"
#include "socket_helper.h"
#include "interface.h"

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>


int cb_head(TransactionTypeDef *ptrans) {
    int status;
    status = check_get_path(ptrans);
    if (status) {
        ptrans->status = status;
        return 0;
    }

    ptrans->status = 200;
    return 0;
}

int cb_get(TransactionTypeDef *ptrans) {
    int rv;
    rv = check_get_path(ptrans);
    if (rv) {
        ptrans->status = rv;
        return 0;
    }
    
    rv = fetch_file(ptrans);
    if (rv) {
        ptrans->status = 500;
        return 0;
    }

    ptrans->status = 200;
    return 0;
}

int cb_post(TransactionTypeDef *ptrans) {
    int rv;
    rv = check_post_path(ptrans);
    if (rv) {
        ptrans->status = rv;
        return 0;
    }
    
    rv = exec_cgi(ptrans);
    if (rv) {
        ptrans->status = 500;
        return 0;
    }

    ptrans->status = 200;
    return 0;
}

int transaction_worker(WorkerThreadTypeDef *pworker) {
    if (!pworker) return -1;

    // const char *MYSTR = "Hello there!\n";
    // printf("Recv from client: \"%s\"\n", pworker->down_buffer);
    // pworker->up_data_size = strlen(MYSTR);
    // pworker->up_data = malloc(pworker->up_data_size + 1);
    // strcpy(pworker->up_data, MYSTR);

    // pworker->up_data = malloc(1000);
    // pworker->up_data_size = 0;
    
    TransactionTypeDef *ptrans;
    ptrans = calloc(1, sizeof(TransactionTypeDef));
    if (!ptrans) return -2;

    int rv = 0;
    rv = protocol_parse(ptrans, pworker);
    if (rv) ptrans->status = 400;

    switch (ptrans->method) {
        case HTTP_HEAD:
            rv = cb_head(ptrans);
        break;
        case HTTP_GET:
            rv = cb_get(ptrans);
        break;
        case HTTP_POST:
            rv = cb_post(ptrans);
        break;
        default:
            ptrans->status = 400;
    }
    if (rv) ptrans->status = 500;

    if (ptrans->status != 200) {
        rv = load_error_page(ptrans);
        if (rv) return -3;
    }
    protocol_pack(ptrans, pworker);

    pworker->log_rsp_size = ptrans->rsp_body_size;
    pworker->log_status = ptrans->status;

    if (ptrans->real_path) free(ptrans->real_path);
    if (ptrans->rsp_header) free(ptrans->rsp_header);
    if (ptrans->rsp_body) free(ptrans->rsp_body);
    free(ptrans);

    // printf("Send to client: \"%s\"\n", pworker->up_buffer);

    return 0;
}

void *transaction_main(void *parg) {
    WorkerThreadTypeDef *pworker;
    int rv;
    if (!parg) {
        printf("Invalid arguments from %s()\n", __FUNCTION__);
        pthread_exit(NULL);
    }
    pworker = (WorkerThreadTypeDef *)parg;
    // printf("thread [%ld]: ", pworker->thread_tid);
    // printf("Starting transaction ...\n");

    while (1) {

        task_get(pworker);

        rv = transmit_down(pworker);
        if (rv != 0) {
            task_shutdown(&(pworker->task));
            worker_reset(pworker);
            printf("thread [%ld]: ", pworker->thread_tid);
            printf("Transaction failed in \"transmit_down\" with return value %d.\n", rv);
            // pthread_exit(NULL);
            task_finish(pworker);
            continue;
        }

        rv = transaction_worker(pworker);
        if (rv != 0) {
            task_shutdown(&(pworker->task));
            worker_reset(pworker);
            printf("thread [%ld]: ", pworker->thread_tid);
            printf("Transaction failed in \"transaction_worker\" with return value %d.\n", rv);
            // pthread_exit(NULL);
            task_finish(pworker);
            continue;
        }

        rv = transmit_up(pworker);
        if (rv != 0) {
            task_shutdown(&(pworker->task));
            worker_reset(pworker);
            printf("thread [%ld]: ", pworker->thread_tid);
            printf("Transaction failed in \"transmit_up\" with return value %d.\n", rv);
            // pthread_exit(NULL);
            task_finish(pworker);
            continue;
        }

        time_t t;
        time(&t);
        char *now = ctime(&t);
        now[strlen(now) - 1] = 0;
        // pthread_mutex_lock(&(pworker->server->log_mut));
        printf("%s:%d [%s] \"%s\" %d %d\n", 
            inet_ntoa(pworker->task.socket_cliaddr.sin_addr), 
            ntohs(pworker->task.socket_cliaddr.sin_port),
            now,
            pworker->log_req_header,
            pworker->log_status,
            pworker->log_rsp_size
        );
        fprintf(pworker->server->log_fh, "%s:%d [%s] \"%s\" %d %d\n", 
            inet_ntoa(pworker->task.socket_cliaddr.sin_addr), 
            ntohs(pworker->task.socket_cliaddr.sin_port),
            now,
            pworker->log_req_header,
            pworker->log_status,
            pworker->log_rsp_size
        );
        // pthread_mutex_unlock(&(pworker->server->log_mut));
        free(pworker->log_req_header);

        task_shutdown(&(pworker->task));
        worker_reset(pworker);
        // printf("thread [%ld]: ", pworker->thread_tid);
        // printf("Transaction finished.\n");
        // pthread_exit(NULL);

        task_finish(pworker);

    }
}

